﻿using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.ObjectModel;
using System.Web.SessionState;
using System.Collections.Generic;
using System.Configuration.Provider;

namespace EuphoricNation.Util
{
    /// <summary>
    /// Authentication class for handling all authentication needs
    /// </summary>
    public static class Authentication
    {
        private const string KEY_PREVPAGE = "LoginPrevPage";

        /// <summary>
        /// Responses when creating a username
        /// </summary>
        public enum UserCreateResponse
        {
            Success,
            UsernameAlreadyExist,
            EmailAlreadyExist,
            InvalidUsername,
            InvalidPassword,
            InvalidEmail,
            Other
        }

        public class User
        {
            private string username;
            private string email;

            public User()
            {
                username = string.Empty;
            }

            public User(string _username, string _email)
            {
                username = _username;
                email = _email;
            }

            public string Username
            {
                get { return username; }
                set { username = value; }
            }

            public string Email
            {
                get { return email; }
                set { email = value; }
            }
        }

        public class Role
        {
            private string role;

            public Role()
            {
                role = string.Empty;
            }

            public Role(string roleName)
            {
                role = roleName;
            }

            public string RoleName
            {
                get { return role; }
                set { role = value; }
            }
        }

#pragma warning disable 0162

        public static string Username
        {
            get
            {
                if (!Constants.DEBUG)
                {
                    if (UserLoggedIn)
                        return HttpContext.Current.User.Identity.Name;
                    else
                        return string.Empty;
                }
                else
                    return "DEBUG";
            }
        }

        public static string Email
        {
            get
            {
                if (!Constants.DEBUG)
                {
                    if (UserLoggedIn)
                    {
                        MembershipUser user = Membership.GetUser();

                        return user.Email;
                    }
                    else
                        return string.Empty;
                }
                else
                    return "DEBUG@DEBUG.COM";
            }
        }

#pragma warning restore 0162

        public static string LoginAddress
        {
            get { return "/templates/Accounts/Login.aspx"; }
        }

        public static bool UserLoggedIn
        {
            get
            {
                if (!Constants.DEBUG)
                    return HttpContext.Current.User.Identity.IsAuthenticated;
                else
                    return true;
            }
        }

        public static void UnlockUser(string username)
        {
            Membership.GetUser(username).UnlockUser();
        }

        /// <summary>
        /// Log a user in
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="password">Password</param>
        /// <returns>Success</returns>
        public static bool Login(string username, string password)
        {
            if (Membership.ValidateUser(username, password))
            {
                FormsAuthentication.SetAuthCookie(username, false);
                return true;
            }
            else
                return false;
        }

        /// <summary>
        /// Request user login, then bring them back to this page
        /// </summary>
        /// <param name="suppressOutput">Suppress further output?</param>
        public static void RequestLogin(bool suppressOutput)
        {
            if (HttpContext.Current.Request.RawUrl.IndexOf(LoginAddress) == -1)
                HttpContext.Current.Session[KEY_PREVPAGE] = "~/" + HttpContext.Current.Request.RawUrl;
            HttpContext.Current.Response.Redirect(LoginAddress, suppressOutput);
        }

        /// <summary>
        /// Request user login, then bring them back to this page
        /// </summary>
        public static void RequestLogin()
        {
            RequestLogin(true);
        }

        /// <summary>
        /// Log the current user out
        /// </summary>
        public static void Logout()
        {
            FormsAuthentication.SignOut();
            HttpContext.Current.Response.Redirect("~/", true);
            HttpContext.Current.Session[KEY_PREVPAGE] = string.Empty;
        }

        /// <summary>
        /// Log the current user out
        /// </summary>
        public static void Logout(string targetURL)
        {
            FormsAuthentication.SignOut();
            HttpContext.Current.Response.Redirect(targetURL, true);
            HttpContext.Current.Session[KEY_PREVPAGE] = string.Empty;
        }

        /// <summary>
        /// Clear the login address
        /// </summary>
        public static void ClearLoginAddress()
        {
            HttpContext.Current.Session[KEY_PREVPAGE] = string.Empty;
        }

        /// <summary>
        /// Get the requested page after login - if any
        /// </summary>
        /// <returns>Requested login page</returns>
        public static string GetRequestedLoginRedirect()
        {
            HttpSessionState Session = HttpContext.Current.Session;

            if (Session["LoginPrevPage"] != null)
                return (string)Session[KEY_PREVPAGE];
            else
                return string.Empty;
        }

        /// <summary>
        /// Get a specific user
        /// </summary>
        /// <param name="username">Username of user</param>
        /// <returns>User</returns>
        public static User GetUser(string username)
        {
            MembershipUser user = Membership.GetUser(username);

            return new User(user.UserName, user.Email);
        }

        /// <summary>
        /// Get a list of all the registered users
        /// </summary>
        /// <returns>List of users</returns>
        public static List<User> GetUsers()
        {
            MembershipUserCollection users = Membership.GetAllUsers();
            List<User> output = new List<User>();

            foreach (MembershipUser user in users)
                output.Add(new User(user.UserName, user.Email));

            return output;
        }

        /// <summary>
        /// Get a list of all the registered users, paging
        /// </summary>
        /// <param name="pageIndex">Index of page wanted</param>
        /// <param name="pageSize">Number of records per page</param>
        /// <param name="numRecords">Number of records on returned page</param>
        /// <returns>List of users</returns>
        public static List<User> GetUsers(int pageIndex, int pageSize, out int numRecords)
        {
            MembershipUserCollection users = Membership.GetAllUsers(pageIndex, pageSize, out numRecords);
            List<User> output = new List<User>();

            foreach (MembershipUser user in users)
                output.Add(new User(user.UserName, user.Email));

            return output;
        }

        /// <summary>
        /// Get a list of all the registered users, paging
        /// </summary>
        /// <param name="pageIndex">Index of page wanted</param>
        /// <param name="pageSize">Number of records per page</param>
        /// <returns>List of users</returns>
        public static List<User> GetUsers(int pageIndex, int pageSize)
        {
            int temp;

            // Sanity checks
            if (pageIndex < 0)
                pageIndex = 0;
            if (pageSize < 1)
                pageSize = 10;

            return GetUsers(pageIndex, pageSize, out temp);
        }

        /// <summary>
        /// Check if a username has already been registered
        /// </summary>
        /// <param name="username">Username to check</param>
        /// <returns>Username already present</returns>
        public static bool UserExists(string username)
        {
            return (Membership.GetUser(username) != null);
        }

        /// <summary>
        /// Get the number of registered users
        /// </summary>
        /// <returns>Number of registered users</returns>
        public static int NumUsers()
        {
            // TODO: OPTIMISE ME!
            MembershipUserCollection users = Membership.GetAllUsers();

            return users.Count;
        }

        /// <summary>
        /// Delete a user
        /// </summary>
        /// <param name="username">Username to delete</param>
        /// <returns>Success</returns>
        public static bool DeleteUser(string username)
        {
            return Membership.DeleteUser(username);
        }

        /// <summary>
        /// Create a user
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="password">Password</param>
        /// <param name="email">Email Address</param>
        /// <param name="firstName">First Name</param>
        /// <param name="secondName">Second Name</param>
        /// <param name="contact">Whether the user wants to be contacted</param>
        /// <returns>Success</returns>
        public static UserCreateResponse CreateUser(string username, string password, string email)
        {
            try
            {
                MembershipUser user = Membership.CreateUser(username, password, email);
            }
            catch (MembershipCreateUserException ex)
            {
                switch (ex.StatusCode)
                {
                    case MembershipCreateStatus.DuplicateEmail:
                        return UserCreateResponse.EmailAlreadyExist;
                    case MembershipCreateStatus.DuplicateUserName:
                        return UserCreateResponse.UsernameAlreadyExist;
                    case MembershipCreateStatus.InvalidEmail:
                        return UserCreateResponse.InvalidEmail;
                    case MembershipCreateStatus.InvalidPassword:
                        return UserCreateResponse.InvalidPassword;
                    case MembershipCreateStatus.InvalidUserName:
                        return UserCreateResponse.InvalidUsername;
                    case MembershipCreateStatus.UserRejected:
                        return UserCreateResponse.Other;
                    default:
                        return UserCreateResponse.Other;
                }
            }

            return UserCreateResponse.Success;
        }

        /// <summary>
        /// Update a user's password
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="password">New password</param>
        /// <returns>Success</returns>
        public static bool UpdateUserPassword(string username, string password)
        {
            MembershipUser user = Membership.GetUser(username);
            string pass = user.ResetPassword();
            user.ChangePassword(pass, password);

            return true;
        }

        /// <summary>
        /// Update a user's email address
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="email">New email</param>
        /// <returns>Success</returns>
        public static bool UpdateEmailAddress(string username, string email)
        {
            MembershipUser user = Membership.GetUser(username);

            user.Email = email;

            return true;
        }

#pragma warning disable 0162

        /// <summary>
        /// Returns whether the logged in user can access the admin panel or not
        /// </summary>
        /// <returns>Whether the user is an admin</returns>
        public static bool UserIsAdmin()
        {
            if (!Constants.DEBUG)
                return UserMemberOfRole(Constants.ROLE_ADMIN);
            else
                return true;
        }

#pragma warning restore 0162

        public static int UserLevel()
        {
            return 0;
        }

        /// <summary>
        /// Returns whether the user can access the admin panel or not
        /// </summary>
        /// <param name="username">Username</param>
        /// <returns>Whether the user is an admin</returns>
        public static bool UserIsAdmin(string username)
        {
            return UserMemberOfRole(username, Constants.ROLE_ADMIN);
        }

#pragma warning disable 0162

        /// <summary>
        /// Returns whether the logged in user is a moderator
        /// </summary>
        /// <returns>Logged in user is moderator</returns>
        public static bool UserIsModerator()
        {
            if (!Constants.DEBUG)
                return UserMemberOfRole(Constants.ROLE_MODERATOR) || UserMemberOfRole(Constants.ROLE_ADMIN);
            else
                return true;
        }

#pragma warning restore 0162

        /// <summary>
        /// Returns whether the specified user is a moderator
        /// </summary>
        /// <param name="username">Username</param>
        /// <returns>Whether the specified user is a moderator</returns>
        public static bool UserIsModerator(string username)
        {
            return UserMemberOfRole(username, Constants.ROLE_MODERATOR) || UserMemberOfRole(username, Constants.ROLE_ADMIN);
        }

        /// <summary>
        /// Get a list of all the registered roles
        /// </summary>
        /// <returns>Registered Roles</returns>
        public static List<Role> GetRoles()
        {
            string[] roles = Roles.GetAllRoles();
            List<Role> output = new List<Role>(roles.Length);
            int i, len = roles.Length;

            for (i = 0; i < len; ++i)
                output.Add(new Role(roles[i]));

            return output;
        }

        /// <summary>
        /// Check if the currently logged in user is a member of a role
        /// </summary>
        /// <param name="role">Role to check</param>
        /// <returns>If user is a member of the role</returns>
        public static bool UserMemberOfRole(string role)
        {
            return HttpContext.Current.User.IsInRole(role);
        }

        /// <summary>
        /// Check if a user is a member of a role
        /// </summary>
        /// <param name="username">Username to check</param>
        /// <param name="role">Role to check</param>
        /// <returns>Whether user is within role</returns>
        public static bool UserMemberOfRole(string username, string role)
        {
            return Roles.IsUserInRole(username, role);
        }

        /// <summary>
        /// Get a list of users within a specific role
        /// </summary>
        /// <remarks>Email field is empty</remarks>
        /// <param name="role">Role name</param>
        /// <returns>List of users</returns>
        public static List<User> GetUsersInRole(string role)
        {
            string[] users = Roles.GetUsersInRole(role);
            List<User> output = new List<User>(users.Length);
            int i, len = users.Length;

            for (i = 0; i < len; ++i)
                output.Add(new User(users[i], string.Empty));

            return output;
        }

        /// <summary>
        /// Assign a user to a role
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="role">Role</param>
        /// <returns>Success</returns>
        public static bool AssignUserToRole(string username, string role)
        {
            bool ex = false;

            if (!Roles.RoleExists(role))
                Roles.CreateRole(role);

            try
            {
                Roles.AddUserToRole(username, role);
            }
            catch (ProviderException)
            {
                ex = true;
            }

            return ex;
        }

        /// <summary>
        /// Remove a user from a specific role
        /// </summary>
        /// <param name="username">Username</param>
        /// <param name="role">Role</param>
        /// <returns>Success</returns>
        public static bool RemoveUserFromRole(string username, string role)
        {
            bool ex = false;

            try
            {
                Roles.RemoveUserFromRole(username, role);
            }
            catch (ProviderException)
            {
                ex = true;
            }

            return !ex;
        }

        /// <summary>
        /// Create a new role
        /// </summary>
        /// <param name="role">Role name to create</param>
        /// <returns>Success</returns>
        public static bool CreateRole(string role)
        {
            try
            {
                Roles.CreateRole(role);
            }
            catch (System.Configuration.Provider.ProviderException)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Delete a role from the system
        /// </summary>
        /// <param name="role">Role to delete</param>
        public static bool DeleteRole(string roleName)
        {
            return Roles.DeleteRole(roleName, false);
        }

        /// <summary>
        /// Rename a role
        /// </summary>
        /// <param name="oldName">Old role name</param>
        /// <param name="newName">New role name</param>
        /// <returns>Success</returns>
        public static bool RenameRole(string oldName, string newName)
        {
            string[] existingUsers = Roles.GetUsersInRole(oldName);

            // First we create the new role, and add the users from the old role to it
            // Then we delete the old role
            // Then we update all references to the old role in the matchup tables to the new role
            if (CreateRole(newName))
            {
                Roles.AddUsersToRole(existingUsers, newName);

                DeleteRole(oldName);
            }
            else
                return false;

            return true;
        }
    }
}
